﻿using System;
using System.Collections.Generic;
using BlobNet.Base.Common;
using BlobNet.Base.DataProviders;
using BlobNet.Base.Enums;

namespace BlobNet.Base.Collections.List
{
    [Serializable]
    internal class BlobPartsPersistentList<TType> : PersistentListBase<TType> //where TType:BlobPart
    {
        private BlobPartsPersistentList(){} 

        public BlobPartsPersistentList(BlobDomain blobDomain, Guid collectionId)
            : this(blobDomain, collectionId,null)
        {
        }
      
       
        public BlobPartsPersistentList(BlobDomain blobDomain, Guid collectionId, IEnumerable<TType> source)
            : base(blobDomain, collectionId, source)
        {}


        #region Overrides of ShPersistentListBase<TType>

        public override TType this[int index]
        {
            get
            {
                if ((uint) index >= (uint) Count)
                    throw new ArgumentOutOfRangeException();
                ITupleList listItem = DataProvider.RetrieveListItem(Domain.EntityId, EntityId, index);

                TType result;

                object obj = listItem != null
                                 ? ObjectBuilder.Default.GetNewInstance(listItem.FullyQualifiedType,Domain, (Guid) listItem.Value)
                                 : null;
                result = (TType) obj;

                return result;
            }
            set
            {
                if ((uint) index >= (uint) Count)
                    throw new ArgumentOutOfRangeException();

                TupleList tuple = null;

                var sh = value as BlobPart;
                if (sh.Domain != Domain)
                    throw new InvalidOperationException("Cant set an BlobPart from another blobDomain");
                tuple = new TupleList(index, EntityId, sh.GetType().FullName, sh.EntityId);

                //reorders the indexes
                DataProvider.RemoveEntityFromCollection(Domain.EntityId, EntityId, index);

                DataProvider.AddEntityToCollection(Domain.EntityId, EntityId, index,
                                                   tuple.FullyQualifiedType, tuple.Value);
                ++Version;
            }
        }

        public BlobPartsPersistentList<TType> GetRange(int index, int count)
        {
            Guid destId = Guid.NewGuid();
            DataProvider.CopyListRange(Domain.EntityId, EntityId, destId, index, count);

            return new BlobPartsPersistentList<TType>(Domain, destId);
        }

        public override void Add(TType item)
        {
            int index = Count;

            TupleList tuple = null;

            var sh = item as BlobPart;
            if (sh.Domain == null)
                sh.Attach(Domain);
            if (sh.Domain != Domain)
                throw new InvalidOperationException("Cant set an BlobPart from another blobDomain");
            sh.StatusStorage = StatusStorageType.Persistent;
            tuple = new TupleList(index, EntityId, item.GetType().FullName, sh.EntityId);


            //TODO: make list per allowed behavior and save the data in different way

            DataProvider.AddEntityToCollection(Domain.EntityId, EntityId, tuple.Position,
                                               tuple.FullyQualifiedType, tuple.Value);
            ++Version;
        }

        public override bool Contains(TType item)
        {
            bool result = false;
            //TODO: optimize to get it from query count

            var sh = item as BlobPart;
            result = DataProvider.RetrieveListItem(Domain.EntityId, EntityId, sh.EntityId) != null;

            return result;
        }

        public override int IndexOf(TType item)
        {
            int result = -1;
            //TODO: optimize to get it from query count


            var sh = item as BlobPart;
            ITupleList retrieveListItem = DataProvider.RetrieveListItem(Domain.EntityId, EntityId,
                                                                        sh.EntityId);
            if (retrieveListItem != null)
                result = retrieveListItem.Position;

            return result;
        }

        public override void Insert(int index, TType item)
        {
            if ((uint) index > (uint) Count)
                throw new ArgumentOutOfRangeException("index");
            TupleList tuple = null;

            var sh = item as BlobPart;
            if (sh.Domain != Domain)
                throw new InvalidOperationException("Cant insert an BlobPart from another blobDomain");
            tuple = new TupleList(index, EntityId, sh.GetType().FullName, sh.EntityId);


            //Stored procedure to reset indexes
            DataProvider.AddEntityToCollection(Domain.EntityId, EntityId, index,
                                               tuple.FullyQualifiedType, tuple.Value);
            ++Version;
        }

        public override void InsertRange(int index, IEnumerable<TType> collection)
        {
            if (collection == null) return;
            if ((uint) index > (uint) Count) throw new ArgumentOutOfRangeException("index");

            int currIdx = index;
            ITupleList tuple = null;
            foreach (TType item in collection)
            {
                var sh = item as BlobPart;
                if (sh.Domain == null)
                    sh.Attach(Domain);
                if (sh.Domain != Domain)
                    throw new InvalidOperationException("Cant insert an BlobPart from another blobDomain");
                sh.StatusStorage = StatusStorageType.Persistent;
                tuple = new TupleList(currIdx++, EntityId, sh.GetType().FullName, sh.EntityId);


                DataProvider.AddEntityToCollection(Domain.EntityId, EntityId, tuple.Position,
                                                   tuple.FullyQualifiedType, tuple.Value);
            }
            ++Version;
        }

        #endregion
    }
}